options(repos = c(CRAN = "https://cloud.r-project.org"))

pkgs <- data.frame(
  package = c(
    "backports","broom","cellranger","cli","compiler","DeclareDesign","digest","dplyr",
    "estimatr","evaluate","fabricatr","farver","fastmap","forcats","Formula","generics",
    "ggplot2","glue","grid","gtable","hms","htmltools","httr","knitr","labeling",
    "lifecycle","magrittr","otel","patchwork","pillar","pkgconfig","purrr","R6",
    "randomizr","RColorBrewer","Rcpp","readr","readxl","rlang","rmarkdown","rstudioapi",
    "S7","scales","stringi","stringr","texreg","tibble","tidyr","tidyselect",
    "tools","tzdb","vctrs","withr","writexl","xfun","yaml"
  ),
  version = c(
    "1.5.0","1.0.12","1.1.0","3.6.5","4.5.2","1.1.0","0.6.39","1.2.0",
    "1.0.6","1.0.5","1.0.2","2.1.2","1.2.0","1.0.1","1.2-5","0.1.4",
    "4.0.2","1.8.0","4.5.2","0.3.6","1.1.4","0.5.9","1.4.8","1.51","0.4.3",
    "1.0.5","2.0.4","0.2.0","1.3.2","1.11.1","2.0.3","1.2.1","2.6.1",
    "1.0.1","1.1-3","1.1.1","2.2.0","1.4.5","1.1.7","2.30","0.18.0",
    "0.2.1","1.4.0","1.8.7","1.6.0","1.39.5","3.3.1","1.3.2","1.2.1",
    "4.5.2","0.5.0","0.7.1","3.0.2","1.5.4","0.56","2.3.12"
  ),
  stringsAsFactors = FALSE
)

base_pkgs <- c("compiler", "grid", "tools")

# ---- sanity check: base package versions depend on R version ----
if (getRversion() != "4.5.2") {
  message("NOTE: Your R version is ", getRversion(), ". The list includes base packages at 4.5.2 ",
          "(compiler/grid/tools). Those come with R and will reflect your R version.")
}

# ---- installer ----
if (!requireNamespace("remotes", quietly = TRUE)) {
  install.packages("remotes")
}

installed_mat <- installed.packages()
installed_version <- function(pkg) {
  if (pkg %in% rownames(installed_mat)) as.character(installed_mat[pkg, "Version"]) else NA_character_
}
is_exact <- function(pkg, ver) {
  iv <- installed_version(pkg)
  !is.na(iv) && utils::compareVersion(iv, ver) == 0
}

install_one <- function(pkg, ver) {
  if (pkg %in% base_pkgs) return(TRUE)
  
  if (is_exact(pkg, ver)) {
    message(sprintf("[OK] %s (%s) already installed", pkg, ver))
    return(TRUE)
  }
  
  message(sprintf("[..] Installing %s (%s)", pkg, ver))
  ok <- FALSE
  
  # try binary first (where applicable), then source
  try({
    remotes::install_version(pkg, version = ver, upgrade = "never",
                             dependencies = FALSE, type = "binary", quiet = TRUE)
  }, silent = TRUE)
  
  if (is_exact(pkg, ver)) return(TRUE)
  
  try({
    remotes::install_version(pkg, version = ver, upgrade = "never",
                             dependencies = FALSE, type = "source", quiet = TRUE)
  }, silent = TRUE)
  
  ok <- is_exact(pkg, ver)
  if (!ok) message(sprintf("[!!] Failed (for now): %s (%s)", pkg, ver))
  ok
}

# ---- multi-pass install (handles ordering without installing unpinned deps) ----
to_install <- pkgs[!(pkgs$package %in% base_pkgs), , drop = FALSE]

max_passes <- 8
for (pass in seq_len(max_passes)) {
  message("\n====================")
  message("Install pass ", pass, " of ", max_passes)
  message("====================")
  
  progress <- FALSE
  remaining <- to_install[!mapply(is_exact, to_install$package, to_install$version), , drop = FALSE]
  if (nrow(remaining) == 0) break
  
  for (i in seq_len(nrow(remaining))) {
    pkg <- remaining$package[i]
    ver <- remaining$version[i]
    ok <- install_one(pkg, ver)
    if (ok) progress <- TRUE
  }
  
  if (!progress) {
    message("\nNo progress in this pass. Remaining packages likely need system libraries, ",
            "compilers (e.g., Rtools on Windows), or a non-CRAN source.")
    break
  }
  
  # refresh installed matrix for the next pass
  installed_mat <- installed.packages()
}

# ---- final report ----
final_remaining <- to_install[!mapply(is_exact, to_install$package, to_install$version), , drop = FALSE]
if (nrow(final_remaining) == 0) {
  message("\nAll requested (non-base) packages are installed at the specified versions.")
} else {
  message("\nThese packages are NOT at the requested versions:")
  print(final_remaining, row.names = FALSE)
}